"""
Copyright 2009, Thomas Dejanovic, Jay Shurtz.

This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
"""

#-----------------------------------------------------------------

id = "$Id: verilog_generator.py 709 2010-09-06 00:04:36Z rabidfruitbat $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.3/hatch/hatch_targets/verilog/verilog_generator.py $
# $Author: rabidfruitbat $
version = " ".join(id.split()[1:3])

#-----------------------------------------------------------------

import re
import math

import to_hatch_structure
from hatch_node import NodeChildren, HatchNode
from hatch_register import Register
from hatch_struct import Pack
from hatch_visitor import *
from verilog_net_dict import *
from verilog_bus import TargetBus
from hatch_constants import TOP_NODE, GLOBAL_NAME, PARENT_ATTRIBUTE, PROPERTIES_ATTRIBUTE

#-----------------------------------------------------------------

class VerilogNode(object):
    """
    """

    def __init__(self, hatchNode, parent=None):
        """ Nets and ports are separate becasue they can refer to each other,
            but ports must be unique and nets must be unique.  There
            is a single dat astructure that holds all this information
            because in any given verilog genration scope the names
            must be unique.

            Only bus slaves are recursively generated.
        """

        self.name = hatchNode.name
        self.type = 'wire' # default to a wire.
        self.parent = None   # The HatchNode that has this object as a child.
        self.hatchNode = hatchNode
        self.childNodes = NodeChildren(self)
        self.verilogSetup = hatchNode.properties.get('rtl', "") # verilog that sets up functions used by other blocks
        self.verilog = ""         # generic verilog, orde does not matter.
        self.verilogReadTerm = "" # empty if this node does not result in a read.

        if parent == None: # we are the top of the tree, generate global data
            # nets and ports are separate becasue they can refer to each
            # other, but ports must be unique and nets must be unique.
            self.ports = VerilogNetBlock()
            self.ports.addType('input_port')
            self.ports.addType('output_port')
            self.nets  = VerilogNetBlock()
            self.nets.addType('reg')
            self.nets.addType('wire')
            self.target = TargetBus(hatchNode, self)

            # add an address decode valid flag..
            self.nets.add("localAddressValid", 'reg', 1);

        else: #share the global definitions for this verilog structure.
            self.ports  = parent.ports
            self.nets   = parent.nets
            self.target = parent.target


        # do a depth first recursion into the hatch data structure and
        # generate an equivalent verilog structure.
        for c in hatchNode.childNodes:
            self.childNodes.append(VerilogNode(c, self))

        # what sort of verilog target are we dealing with?
        if self.target.function == 'slave':
            # OK now we generate verilog for this node.
            self.processHatchNode(hatchNode)
            if self.hatchNode.__class__.__name__ == Register.__name__:
                self.buildRegister()
                self.buildRegisterReadTerm()
            if self.hatchNode.__class__.__name__ == Pack.__name__:
                self.buildPack()
                self.buildRegisterReadTerm()

    def code(self):
        """ return a string containg the verilog for this node.
        """
        codeString = ""
        if self.target.function == 'slave':
            codeString += self.getModuleDefinition()
            codeString += self.getPortModeDefinition()
            codeString += self.getNetDefinition()
            codeString += self.getVerilogSetup()
            codeString += self.getVerilog()
            codeString += self.getverilogReadBlock()
            codeString += "endmodule // %s\n"%(self.name)

        return codeString

    def address_defines(self):
        """ return a string containing address defines for all the registers.
        """
        codeString = ""
        codeString += "  //\n  // register address defines for %s\n  //\n"%(self.name)
        codeString += self.getAddressDefine()
        codeString += "  // - - - - - - - - end of defines - - - - - - - -\n"
        return codeString

    def getAddressDefine(self):
        if self.hatchNode.__class__.__name__ == Pack.__name__:
            return "  `define %s %d\n"%(self.hatchNode.properties[GLOBAL_NAME],
                                        self.hatchNode.properties['base_address'])
        elif self.hatchNode.__class__.__name__ == Register.__name__:
            return "  `define %s %d\n"%(self.hatchNode.properties[GLOBAL_NAME], 
                                        self.hatchNode.properties['base_address'])
        else:
            r = ""
            for c in self.childNodes:
                r += c.getAddressDefine()
            return r

    def getVerilogSetup(self):
        if len(self.verilogSetup) > 0:
            codeString  = "\n  // - - - - - - - - verilog setup for %s - - - - - - - -\n"%(self.name)
            codeString += self.verilogSetup
            codeString += "  // - - - - - - - - end of verilog setup - - - - - - - -\n"
        else:
            codeString = ""
        for c in self.childNodes:
            codeString += c.getVerilogSetup()
        return codeString

    def getVerilog(self):
        codeString = self.verilog
        for c in self.childNodes:
            codeString += c.getVerilog()
        return codeString

    def getModuleDefinition(self):
        """ return the module definition string.
        """
        codeString = "module %s_hatch\n"%(self.name)
        codeString += "  (// Outputs\n  "
        codeString += ", ".join(self.ports.type['output_port'])
        if len(self.ports.type['output_port']) > 0:
            codeString += ",\n"
        else:
            codeString += "\n"
        codeString += "   // Inputs\n  "
        codeString += ", ".join(self.ports.type['input_port'])
        codeString += "\n  );\n"
        return codeString

    def getPortModeDefinition(self):
        """ return the port mode definition string.
        """
        codeString  = "\n  //-----------------------------------------\n"
        codeString += "  // Outputs\n"
        for p in self.ports.type['output_port']:
            codeString += "  output "
            if self.ports[p].width > 1:
                codeString += "[%d:0] "%(self.ports[p].width-1)
            codeString += "%s;\n"%(p)
        codeString += "  //-----------------------------------------\n"
        codeString += "  // Inputs\n"
        for p in self.ports.type['input_port']:
            codeString += "  input "
            if self.ports[p].width > 1:
                codeString += "[%d:0] "%(self.ports[p].width-1)
            codeString += "%s;\n"%(p)
        codeString += "  //-----------------------------------------\n"
        return codeString

    def getNetDefinition(self):
        """ return wire and reg definition string.
        """
        codeString = ""
        for t in ['wire', 'reg']:
            codeString += "  //-----------------------------------------\n"
            codeString += "  // %s\n"%(t)
            for p in self.nets.type[t]:
                codeString += "  %s "%(t)
                if self.nets[p].signed:
                    codeString += "signed "
                if self.nets[p].width > 1:
                    codeString += "[%d:0] "%(self.nets[p].width-1)
                codeString += "%s;\n"%(p)
        codeString += "  //-----------------------------------------\n"
        return codeString


    def readRegisterSensitivityList (self):

        """return a list of signals to put in the sensitivity list for the
        CPU read function"""

        if self.hatchNode.__class__.__name__ == Pack.__name__:
            return [self.name]
        elif self.hatchNode.__class__.__name__ == Register.__name__:
            return [self.name]
        else:
            r = []
            for c in self.childNodes:
                r += c.readRegisterSensitivityList()
            return r

    def getverilogReadBlock(self):

        sensitivityList  = [self.target.addr]
        sensitivityList += self.readRegisterSensitivityList()
        codeString  = "\n  // - - - - - - - - rtl for read block - - - - - - - -\n"
        codeString += "  always @ ("
        codeString += ", ".join(sensitivityList)
        codeString += ") begin\n"
        codeString += "    localAddressValid <= 1'd1;\n"
        codeString += "    case(%s%s)\n"%(self.target.addr, self.target.localAddrWidthString)
        codeString += self.getverilogReadTerm()
        codeString += """      default : begin
        localAddressValid <= 1'd0;
        %s <= %s;
      end\n"""%(self.target.readData, value_string(0, self.target.dataWidth))
        codeString += "    endcase\n"
        codeString += "  end\n"
        codeString += "  // - - - - - - - - end of rtl for read block - - - - - - - -\n"
        return codeString

    def getverilogReadTerm(self):
        codeString = self.verilogReadTerm
        for c in self.childNodes:
            codeString += c.getverilogReadTerm()
        return codeString


    # TODO - take this out, it is already checked in the HatchNode class.
    def check_parent(self, val):
        """ From HatchNode - if you are a child you cannot also be my parent
            and if I already have a parent you cannot assigne a nother
            parent.
        """
        try:
            if val.has_parent(self): # or self.has_child(val): # Don't need both.
                raise AttributeError, hatchNodeErrorString(self.hatchNode, "Cannot set parent to a value in own childNodes, this would be circular.")
        except AttributeError, e:    # val is probably None.
            pass
        if self.parent is not None:  # Can still hack around this with self.__dict__['parent'] = 'foobar'
            raise AttributeError, "Parent value already set to " + str(self.parent)

    def has_parent(self, p):
        """ Return True if any parent in the tree above this is the passed in HatchNode.
        """
        retVal = False
        if self.parent is not None:
            retVal = self.parent == p or self.parent.has_parent(p)
        return retVal


    def processHatchNode(self, hatchNode):
        """ The brains of this need to be extended such that muxes etc do not
            do a depth first search.  These mor interesting nodes just
            need to know enaugh about thier children to do address
            decoding etc.
        """

        self.setIOPorts(hatchNode)
        self.setNets(hatchNode)
        # XXX Add more functions here.


    def setNets(self, hatchNode):
        """ Create all the internal nets required to implement the software
            and auto functions of this node.
            All registers are readable by default and therefore should exist!
             str(hatchNode) is the storage element.
             str(hatchNode)+"_sw_wen" is a signal used to update the storage element.
             str(hatchNode)+"_sw_trigger" is a signal generated by writing to this register.
             str(hatchNode)+"_sw_rd" is a signal generated when the register is read and may not actualy generate any action.

            Exisiting ports defined by the user also need to be added.

            TODO - this could be rolled into generate register so the
            wires are declared when they are referenced.
        """

        if self.hatchNode.__class__.__name__ == Pack.__name__:
            # add a wire to build the pack.  
            self.nets.add(str(hatchNode), 'wire', hatchNode.properties['bit_width'])
        elif self.hatchNode.__class__.__name__ == Register.__name__:
            hw = self.hatchNode.properties.get('hw','')
            sw = self.hatchNode.properties.get('sw','')
            auto = self.hatchNode.properties.get('auto','')
            signed = self.hatchNode.has_property('signed')
            # we need a storge element if the register is writable by HW, SW or an auto funtion.
            if hatchNode.has_property('external'):
                # no storage.
                self.nets.add(str(hatchNode), 'wire', hatchNode.properties['bit_width'], signed)
                self.type = 'wire'
                # sanity check.
                if (auto != ''):
                    raise AssertionError, "%s defined as external but has auto functions which require internal storage."%(self.name)
            elif (auto != '' or 'w' in hw or 'i' in hw or 's' in hw or 'c' in hw or 'p' in hw or 'w' in sw):
                # the storage element.
                self.nets.add(str(hatchNode), 'reg', hatchNode.properties['bit_width'], signed)
                # new value for the storage element.
                self.nets.add(str(hatchNode)+"_next", 'wire', hatchNode.properties['bit_width'], signed)
                self.type = 'reg'
            else :
                # no storage.
                self.nets.add(str(hatchNode), 'wire', hatchNode.properties['bit_width'], signed)
                self.type = 'wire'

            # sanitise the trigger count property
            if self.hatchNode.has_property('trigger_count') :
                t = int(self.hatchNode.properties['trigger_count'])
                if t > 1:
                    w = int(math.ceil(math.log(abs(t)) / math.log(2)))
                    self.hatchNode.properties['trigger_count'] = t
                    self.hatchNode.properties['trigger_count_width'] = w
                else :
                    del self.hatchNode.properties['trigger_count']

            # 'sw' funcions.
            if 'w' in sw:
                self.nets.add(str(hatchNode)+"_int_wen", 'wire', 1)
            if 't' in sw:
                self.nets.add(str(hatchNode)+"_sw_trigger", 'wire', 1)
                sto = self.hatchNode.properties.get('sw_trigger_on', '')
                if self.hatchNode.has_property('key_value'):
                    if sto != '':
                        raise AssertionError, hatchNodeErrorString(hatchNode, "Software key trigger with any other trigger type is not currently supported.")
                    self.nets.add(str(hatchNode)+"_sw_key_trigger", 'wire', 1)
                if 'w' in sto:
                    if not 'w' in sw:
                        raise AssertionError, hatchNodeErrorString(hatchNode, "Software trigger on write for register that is not writable.")
                    self.nets.add(str(hatchNode)+"_sw_write_trigger", 'wire', 1)
                if 'r' in sto:
                    self.nets.add(str(hatchNode)+"_int_ren", 'wire', 1)
                    self.nets.add(str(hatchNode)+"_sw_read_trigger", 'wire', 1)
                if self.hatchNode.has_property('trigger_count'):
                    self.nets.add(str(hatchNode)+"_sw_trigger_int", 'wire', 1)
                    self.nets.add(str(hatchNode)+"_sw_trigger_count", 'reg',
                                  self.hatchNode.properties['trigger_count_width'])

            # 'auto' property.
            if self.hatchNode.has_property('auto_inc_on') and 'i' not in auto:
                auto += 'i'
                self.hatchNode.properties['auto'] = auto
            if self.hatchNode.has_property('auto_dec_on') and 'd' not in auto:
                auto += 'd'
                self.hatchNode.properties['auto'] = auto
            if self.hatchNode.has_property('auto_set_on') and 's' not in auto:
                auto += 's'
                self.hatchNode.properties['auto'] = auto
            if self.hatchNode.has_property('auto_clear_on') and 'c' not in auto:
                auto += 'c'
                self.hatchNode.properties['auto'] = auto
            if self.hatchNode.has_property('auto_preset_on') and 'p' not in auto:
                auto += 'p'
                self.hatchNode.properties['auto'] = auto


            if 'i' in auto:
                self.nets.add(str(hatchNode)+"_auto_inc", 'wire', 1)
            if 'd' in auto:
                self.nets.add(str(hatchNode)+"_auto_dec", 'wire', 1)
            if 's' in auto:
                self.nets.add(str(hatchNode)+"_auto_set", 'wire', 1)
            if 'c' in auto:
                self.nets.add(str(hatchNode)+"_auto_clear", 'wire', 1)
            if 'p' in auto:
                self.nets.add(str(hatchNode)+"_auto_preset", 'wire', 1)
            if 't' in auto :
                self.nets.add(str(hatchNode)+"_auto_trigger", 'wire', 1)
                if self.hatchNode.has_property('trigger_count') :
                    self.nets.add(str(hatchNode)+"_auto_trigger_int", 'wire', 1)
                    self.nets.add(str(hatchNode)+"_auto_trigger_count", 'reg',
                                  self.hatchNode.properties['trigger_count_width'])
            # TODO - more auto functions.


    def convertStringToPorts (self, hatchNode, targetType, s):
        """ Convert exisiting port strings defined by the user.

            port widths defined with non zero bases are not supported.
            ports must also be big endian.
        """

        s = s.replace('\n','')
        s = s.replace('[', ' [')
        while s.rfind('  ') > 0:
            s = s.replace('  ',' ')
        for p in s.split(','):
            # check for a port size.
            pl = p.split()
            n = pl[0]
            w = 1
            signed = False
            if len(pl) > 1:
                # do we have a signed specifier?
                if  pl[1] == 'signed':
                    signed = True
                    pl = pl[1:]

                if len(pl) > 1:
                    # width is a range of the form "[z:y]"
                    m1 = re.match("^\s*\[\s*(?P<top>\d+)\s*:\s*(?P<bot>\d+)\s*\]\s*$",pl[1])
                    t = 0
                    b = 0
                    if m1 :
                        try:
                            t = int(m1.group('top'))
                        except ValueError:
                            raise AssertionError, hatchNodeErrorString(hatchNode, "Could not convert net range for " + s)
                        try:
                            b = int(m1.group('bot'))
                        except ValueError:
                            raise AssertionError, hatchNodeErrorString(hatchNode, "Could not convert net range for " + s)
                        if b != 0:
                            raise AssertionError, hatchNodeErrorString(hatchNode, "Base of net range must be 0, found non 0 value for "+ s)
                    else:
                        raise AssertionError, hatchNodeErrorString(hatchNode, "Could not convert net range for " + s)
                    w = t + 1
            self.ports.add(n, targetType, w, signed)


    def setIOPorts(self, hatchNode):
        """ Convert exisiting port, wire and reg definitions from the user to
            an internal data structure.

            internal singals cannot have the same name as input or
            output ports.

            in ADDITION, geenrate an internal registers, wires or
            ports based onthe register functions being implemented in
            this block.
        """
        for p in ['reg', 'wire', 'input_ports', 'output_ports']:
            try:
                value = hatchNode.properties[p]
                if type(value) is str:
                    # the existing definition is a string, lets convert it.
                    self.convertStringToPorts(hatchNode, p[:-1], value) # remove the 's' from *_ports
                else:
                    raise AssertionError, hatchNodeErrorString(hatchNode, "Internal error - setIOPorts found type" + str(type(value)) + " for property " + p + " when expecting str." )
            except KeyError, e:
                pass # nothing to do.

        signed = self.hatchNode.has_property('signed')

        # External properties, this register is not implemented here.
        if hatchNode.has_property('external'):  # TODO - use value : if hatchNode.properties['external']: ( == True ) ?
            self.ports.add( str(hatchNode)+"_in",  'input_port',  hatchNode.properties['bit_width'], signed)

            # add write ports if the register is writable
            value = hatchNode.properties.get('sw','')
            if 'w' in value:
                self.ports.add( str(hatchNode)+"_out", 'output_port', hatchNode.properties['bit_width'], signed)
                self.ports.add( str(hatchNode)+"_wen",  'input_port',  1)

        else:   # Only if NOT external, i.e. hdl will be generated.
            # Error detection/correction.
            if hatchNode.has_property('auto_error_detect') or hatchNode.has_property('auto_error_correct'):
                self.ports.add( str(hatchNode)+"_soft_error", 'output_port',   1)

            # 'hw' property.
            try:
                value = hatchNode.properties['hw']
            except KeyError, e:
                pass    # Property does not exist, skip it.
            else:
                if 'r' in value:
                    self.ports.add(str(hatchNode)+"_out",     'output_port', hatchNode.properties['bit_width'], signed)
                    self.nets.add(str(hatchNode)+"_out",      'wire',        hatchNode.properties['bit_width'], signed)
                if 'w' in value:
                    self.ports.add( str(hatchNode)+"_in",     'input_port', hatchNode.properties['bit_width'], signed)
                    self.nets.add(str(hatchNode)+"_in",       'wire',       hatchNode.properties['bit_width'], signed)
                    self.ports.add( str(hatchNode)+"_wen",     'input_port', 1)
                    self.nets.add(str(hatchNode)+"_wen",       'wire',       1)
                if 'd' in value:
                    self.ports.add( str(hatchNode)+"_dec",    'input_port', 1)
                    self.nets.add(str(hatchNode)+"_dec",      'wire',       1)
                if 'i' in value:
                    self.ports.add( str(hatchNode)+"_inc",    'input_port', 1)
                    self.nets.add(str(hatchNode)+"_inc",      'wire',       1)
                if 'c' in value:
                    self.ports.add( str(hatchNode)+"_clear",  'input_port', 1)
                    self.nets.add(str(hatchNode)+"_clear",    'wire',       1)
                if 's' in value:
                    self.ports.add( str(hatchNode)+"_set",    'input_port', 1)
                    self.nets.add(str(hatchNode)+"_set",      'wire',       1)
                if 'p' in value:
                    self.ports.add( str(hatchNode)+"_preset", 'input_port', 1)
                    self.nets.add(str(hatchNode)+"_preset",   'wire',       1)


    def buildPack(self):
        """ build the verilog for this pack.  This is just a convienience function to help the read decode.
        """

        self.verilog += "\n  // - - - - - - - - rtl for pack %s - - - - - - - -\n\n"%(self.name) + self.verilog

        self.verilog += "  assign %s = {"%(self.name)
        for c in self.childNodes[:-1]:
            self.verilog += "%s, "%(c.name)
        self.verilog += "%s};\n"%(self.childNodes[-1].name)

        value = self.hatchNode.properties.get('hw','')
        if 'r' in value:
            self.verilog += "  assign %s_out = %s;\n"%(self.name, self.name)

        self.verilog += "\n  // - - - - - - - - end of rtl for pack %s - - - - - - - -\n\n"%(self.name)

    def buildRegister(self):
        """ build the verilog for this node if we are a leaf node.  We can
            assume the address decoding with be dome somewhere else.
            This is just the basig register creation function.
        """

        self.verilog += "\n  // - - - - - - - - rtl for register %s - - - - - - - -\n\n"%(self.name) + self.verilog

        triggersUsed = False

        hw = self.hatchNode.properties.get('hw','')
        sw = self.hatchNode.properties.get('sw','')
        auto = self.hatchNode.properties.get('auto','')
        
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the sw write and trigger function.
        #

        if (sw != '') :
            if  ('w' in sw):
                # implement the software write enable signal used for both
                # writing and triggering the register.
                self.verilog += "  assign %s_int_wen = %s & "%(self.name, self.target.writeEn)
                # add software load enable.
                if self.hatchNode.has_property('sw_load_enable') :
                    self.verilog += "%s & "%(self.hatchNode.properties['sw_load_enable'])
                self.verilog += "(%s%s == "%(self.target.addr, self.target.localAddrWidthString)
                self.verilog += "%s);\n"%addr_string(self.hatchNode.properties['base_address'],
                                                     self.target.localAddressWidth)

            if  't' in sw:
                triggersUsed = True
                # build the trigger terms.

                if self.hatchNode.has_property('key_value'):
                    # this is a one bit trigger output.
                    self.verilog += "  assign %s_sw_key_trigger = %s_int_wen & ("%(self.name, self.name)
                    self.verilog += "%s == "%(value_string(self.hatchNode.properties['key_value'],
                                                           self.hatchNode.properties['bit_width']))
                    if self.hatchNode.properties['bit_width'] > 1:
                        # pick out the correct bits on the taget bus data
                        self.verilog += "%s[%d:%d]);\n"%(self.target.writeData,
                                                         self.hatchNode.properties['bit_width'] + self.hatchNode.properties['base_bit'] - 1,
                                                         self.hatchNode.properties['base_bit'])
                    else:
                        # pick out the correct target bus data bit.
                        self.verilog += "%s[%d]);\n"%(self.target.writeData, self.hatchNode.properties['base_bit'])


                sto = self.hatchNode.properties.get('sw_trigger_on', '')
                if 'w' in sto:
                    self.verilog += "  assign %s_sw_write_trigger = %s_int_wen;\n"%(self.name, self.name)

                if 'r' in sto:
                    # build the read decode
                    self.verilog += "  assign %s_int_ren = %s & "%(self.name, self.target.readEn)
                    self.verilog += "(%s%s == "%(self.target.addr, self.target.localAddrWidthString)
                    self.verilog += "%s);\n"%addr_string(self.hatchNode.properties['base_address'],
                                                         self.target.localAddressWidth)
                    self.verilog += "  assign %s_sw_read_trigger = %s_int_ren;\n"%(self.name, self.name)

                # pull all these together.
                if self.hatchNode.has_property('trigger_count'):
                    self.verilog += "  assign %s_sw_trigger_int = "%(self.name)
                else:
                    self.verilog += "  assign %s_sw_trigger = "%(self.name)

                link_str = ""
                if self.hatchNode.has_property('key_value'):
                    self.verilog += "%s %s_sw_key_trigger"%(link_str, self.name)
                    link_str = "|"
                if 'w' in sto:
                    self.verilog += "%s %s_sw_write_trigger"%(link_str, self.name)
                    link_str = "|"
                if 'r' in sto:
                    self.verilog += "%s %s_sw_read_trigger"%(link_str, self.name)
                    link_str = "|"
                self.verilog += ";\n"

                # build the trigger count if required.
                if self.hatchNode.has_property('trigger_count') :
                    self.verilog += "  always @ (posedge %s) if (%s) begin\n"%(self.target.clk, self.target.clkEn)
                    self.verilog += "    if (%s)\n"%(self.target.reset)
                    self.verilog += "      %s_sw_trigger_count <= %d'd0;\n"%(self.name,
                                                                             self.hatchNode.properties['trigger_count_width'])
                    self.verilog += "    else\n"
                    self.verilog += "      %s_sw_trigger_count <= %s_sw_trigger_int ? "%(self.name, self.name)
                    self.verilog += "%s :\n"%(value_string(self.hatchNode.properties['trigger_count'],
                                                           self.hatchNode.properties['trigger_count_width']))
                    self.verilog += "                          %s_sw_trigger_count - "%(self.name)
                    self.verilog += "{%d'd0, |%s_sw_trigger_count};\n"%(self.hatchNode.properties['trigger_count_width'] - 1,
                                                                      self.name)
                    self.verilog += "  end\n"

                    self.verilog += "  assign %s_sw_trigger = %s_sw_trigger_int | (|%s_sw_trigger_count);\n"%(self.name,
                                                                                                              self.name,
                                                                                                              self.name)


        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto trigger function.
        #
        # this sets the trigger signal when the contents of the
        # register change to the value described in the
        # auto_trigger_on parameter.
        
        if 't' in auto :
            triggersUsed = True

            # figure out what triger term we are gnerating.
            if self.hatchNode.has_property('trigger_count'):
                self.verilog += "  assign %s_auto_trigger_int = "%(self.name)
            else:
                self.verilog += "  assign %s_auto_trigger = "%(self.name)

            self.verilog += "(%s != %s) & "%(self.name,
                                             value_string(self.hatchNode.properties['auto_trigger_value'],
                                                          self.hatchNode.properties['bit_width']))
            self.verilog += "(%s_next == %s);\n"%(self.name,
                                                  value_string(self.hatchNode.properties['auto_trigger_value'],
                                                               self.hatchNode.properties['bit_width']))

            # build the trigger count if required.
            if self.hatchNode.has_property('trigger_count') :
                self.verilog += "  always @ (posedge %s) if (%s) begin\n"%(self.target.clk, self.target.clkEn)
                self.verilog += "    if (%s)\n"%(self.target.reset)
                self.verilog += "      %s_auto_trigger_count <= %d'd0;\n"%(self.name,
                                                                         self.hatchNode.properties['trigger_count_width'])
                self.verilog += "    else\n"
                self.verilog += "      %s_auto_trigger_count <= %s_auto_trigger_int ? "%(self.name, self.name)
                self.verilog += "%s :\n"%(value_string(self.hatchNode.properties['trigger_count'],
                                                       self.hatchNode.properties['trigger_count_width']))
                self.verilog += "                          %s_auto_trigger_count - "%(self.name)
                self.verilog += "{%d'd0, |%s_auto_trigger_count};\n"%(self.hatchNode.properties['trigger_count_width'] - 1,
                                                                  self.name)
                self.verilog += "  end\n"

                self.verilog += "  assign %s_auto_trigger = %s_auto_trigger_int | (|%s_auto_trigger_count);\n"%(self.name,
                                                                                                          self.name,
                                                                                                          self.name)

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto increment function.
        #
        # this sets an internal auto increment signal when the
        # condition in the auto_inc_on condition is met.

        if 'i' in auto :
            self.verilog += "  assign %s_auto_inc = "%(self.name)

            if self.hatchNode.has_property('auto_inc_on') :
                self.verilog += "%s;\n"%(self.hatchNode.properties['auto_inc_on'])
            else:
                # free running down counter.
                self.verilog += "1'd1;\n"
                
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto decrement function.
        #
        # this sets an internal auto decrement signal when the
        # condition in the auto_dec_on condition is met.

        if 'i' in auto :
            self.verilog += "  assign %s_auto_dec = "%(self.name)

            if self.hatchNode.has_property('auto_dec_on') :
                self.verilog += "%s;\n"%(self.hatchNode.properties['auto_dec_on'])
            else:
                # free running up counter.
                self.verilog += "1'd1;\n"
                
        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto set function.
        #
        # this sets an internal auto set signal when the
        # condition in the auto_dec_on condition is met.

        if 's' in auto :
            self.verilog += "  assign %s_auto_set = "%(self.name)

            if self.hatchNode.has_property('auto_set_on') :
                self.verilog += "%s;\n"%(self.hatchNode.properties['auto_set_on'])
            else:
                # does not make sense.
                raise AssertionError, hatchNodeErrorString(self.hatchNode, """auto set specified without auto_set_on""")

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto clear function.
        #
        # this sets an internal auto clear signal when the
        # condition in the auto_dec_on condition is met.

        if 'c' in auto :
            self.verilog += "  assign %s_auto_clear = "%(self.name)

            if self.hatchNode.has_property('auto_clear_on') :
                self.verilog += "%s;\n"%(self.hatchNode.properties['auto_clear_on'])
            else:
                # does not make sense.
                raise AssertionError, hatchNodeErrorString(self.hatchNode, """auto clear specified without auto_clear_on""")

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the auto preset function.
        #
        # this sets an internal auto preset signal when the
        # condition in the auto_dec_on condition is met.

        if 's' in auto :
            self.verilog += "  assign %s_auto_preset = "%(self.name)

            if self.hatchNode.has_property('auto_preset_on') :
                self.verilog += "%s;\n"%(self.hatchNode.properties['auto_preset_on'])
            else:
                # does not make sense.
                raise AssertionError, hatchNodeErrorString(self.hatchNode, """auto preset specified without auto_preset_on""")

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # the external port connection.
        #
            
        if (self.hatchNode.has_property('external') and 'w' in sw) :
            self.verilog += "  assign %s_out = %s[%d:%d];\n"%(self.name,
                                                              self.target.writeData,
                                                              self.hatchNode.properties['bit_width'] + self.hatchNode.properties['base_bit'] - 1,
                                                              self.hatchNode.properties['base_bit'])
            self.verilog += "  assign %s_wen = %s_int_wen;\n\n"%(self.name, self.name)

        # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        #
        # do we have a register to build?
        #

        # XXXX thomasd - tony use the fact that we did:
        # self.nets.add(str(hatchNode), 'reg', hatchNode.properties['bit_width'], signed)
        # to define the fact that there was a register queud for creation.  do something like
        # if self.nets[str(hatchNode)]['type'] == 'reg': 
        # probabaly leave in the 'and not self.hatchNode.has_property('external'))' part of the test.

#        if (('w' in sw or 'w' in hw or auto != '') and not self.hatchNode.has_property('external')) :
        if (self.nets[self.hatchNode.name].type == 'reg' and not self.hatchNode.has_property('external')) :
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the hardware read / write port connection.
            #
            
            if 'r' in hw:
                self.verilog += "  assign %s_out = %s;\n"%(self.name, self.name)

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # build the registr_name_next value using an assign and
            # the ternary opperator to propogate X's and avoid having
            # to compute a sensitivity list which can be tricky if
            # some of the terms used contain expressions.
            #

            s = "  assign %s_next = "%(self.name)
            indent = ""
            self.verilog += s

            # precidence matters here.  after some consideration the
            # precidence of opperations is:
            # 1. CPU
            # 2. clear
            # 3. set
            # 4. preset
            # 5. inc / dec

            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the cpu write term.
            #

            if 'w' in sw :
                self.verilog += indent
                indent = " " * len(s)
                if self.hatchNode.properties['bit_width'] > 1:
                    # pick out the correct bits on the taget bus data
                    self.verilog += "%s_int_wen ? %s[%d:%d]:\n"%(self.name,
                                                                self.target.writeData,
                                                                self.hatchNode.properties['bit_width'] + self.hatchNode.properties['base_bit'] - 1,
                                                                self.hatchNode.properties['base_bit'])
                else:
                    # this is one bit wide.
                    self.verilog += "%s_int_wen ? %s[%d]:\n"%(self.name,
                                                             self.target.writeData,
                                                             self.hatchNode.properties['base_bit'])
                    
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the hardware write term.
            #
            
            if self.hatchNode.has_property('hw') and 'w' in self.hatchNode.properties['hw']:
                self.verilog += "%s_wen ? %s_in:\n"%(self.name, self.name)
                
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the clear term.
            #

            if self.hatchNode.has_property('auto') and 'c' in self.hatchNode.properties['auto']:
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "    %s_auto_clear ? %d'h0 : \n"%(self.hatchNode.name,
                                                                  self.hatchNode.properties['bit_width'])

            if self.hatchNode.has_property('hw') and 'c' in self.hatchNode.properties['hw']:
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s_clear ? %d'h0 : \n"%(self.name, self.hatchNode.properties['bit_width'])

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the set term.
            #

            if self.hatchNode.has_property('auto') and 's' in self.hatchNode.properties['auto']:
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s_auto_set ? %d'h%x : \n"%(self.hatchNode.name,
                                                             self.hatchNode.properties['bit_width'],
                                                             2 ** self.hatchNode.properties['bit_width'] - 1)

            if self.hatchNode.has_property('hw') and 's' in self.hatchNode.properties['hw']:
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s_set ? %d'h%x : \n"%(self.name,
                                                        self.hatchNode.properties['bit_width'],
                                                        2 ** self.hatchNode.properties['bit_width'] - 1)
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the preset term.
            #

            if self.hatchNode.has_property('auto') and 'p' in self.hatchNode.properties['auto']:
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s _auto_preset? %s : \n"%(self.hatchNode.name,
                                                            value_string(self.hatchNode.properties['default'],
                                                                         self.hatchNode.properties['bit_width']))

            if self.hatchNode.has_property('hw') and 'p' in self.hatchNode.properties['hw']:
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s_preset ? %s : \n"%(self.name,
                                                       value_string(self.hatchNode.properties['default'],
                                                                    self.hatchNode.properties['bit_width']))
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the inc / dec term.
            #
            # an inc + dec at the same time is a NOP

            hi = 'i' in self.hatchNode.properties.get('hw','')
            hd = 'd' in self.hatchNode.properties.get('hw','')
            ai = 'i' in self.hatchNode.properties.get('auto','')
            ad = 'd' in self.hatchNode.properties.get('auto','')

            iTerm = ""
            if hi and ai :
                iTerm = "(%s_inc | %s_auto_inc)"%(self.name, self.name)
            elif hi:
                iTerm = "%s_inc"%(self.name)
            elif ai:
                iTerm = "%s_auto_inc"%(self.name)

            dTerm = ""
            if hd and ad :
                dTerm = "(%s_dec | %s_auto_dec)"%(self.name, self.name)
            elif hd:
                dTerm = "%s_dec"%(self.name)
            elif ad:
                dTerm = "%s_auto_dec"%(self.name)

            if (hi or ai) and (hd or ad):
                # we have both an inc and a dec term so we need to catch it.
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "(%s & %s) ? %s : \n"%(iTerm, dTerm, self.name)

            if hi or ai:
                # we have an inc term.
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s ? %s + %d'h1 : \n"%(iTerm, self.name, self.hatchNode.properties['bit_width'])

            if hd or ad:
                # we have a dec term.
                self.verilog += indent
                indent = " " * len(s)
                self.verilog += "%s ? %s - %d'h1 : \n"%(dTerm, self.name, self.hatchNode.properties['bit_width'])


            #
            # the default value.
            #

            self.verilog += indent
            self.verilog += "%s;\n"%(self.name)

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the synchronous always block.
            #

            if  (self.hatchNode.has_property('hw') and re.match("^.*[wscidp]+.*$", self.hatchNode.properties['hw'])) or self.hatchNode.has_property('auto'):
                # there is some automatic or external HW manipulation
                # of the register so we cannot use the gated clock for
                # this register.
                self.verilog += "  always @ (posedge %s) if (%s) begin\n"%(self.target.clk,
                                                                           self.target.clkEn)
            else:
                # this is a vanila config register. don't clock it
                # when we are not writing to it.
                self.verilog += "  always @ (posedge %s) if (%s) begin\n"%(self.target.gatedClk,
                                                                       self.target.gatedClkEn)

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the reset term, load the default value for the register.
            #
            
            self.verilog += "    if (%s)\n"%(self.target.reset)
            self.verilog += "      %s <= %s;\n"%(self.name,
                                                 value_string(self.hatchNode.properties['default'],
                                                              self.hatchNode.properties['bit_width']))
            
            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            #
            # the register guts.
            #
            self.verilog += "    else\n"
            self.verilog += "      %s <= %s_next;\n"%(self.name, self.name)

            # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

            # end of synchronous block
            self.verilog += "  end\n"

        else:
            if (self.hatchNode.has_property('external')):
                # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                #
                # We have an external register that is only readable by sw.
                #
                self.verilog += "  assign %s = %s_in;\n"%(self.name, self.name)

            else:
                # - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
                #
                # This is a read only register.
                #

                self.verilog += "  // This is a read only register.\n"
                self.verilog += "  assign %s = %s;\n"%(self.name,
                                                        value_string(self.hatchNode.properties['default'],
                                                                     self.hatchNode.properties['bit_width']))
                if ('r' in hw) :
                    self.verilog += "  assign %s_out = %s;\n"%(self.name, self.name)

        self.verilog += "\n  // - - - - - - - - end of rtl for register %s - - - - - - - -\n\n"%(self.name)


    def buildRegisterReadTerm(self):

        """ Generate the verilog required to implement read function.
        """

        # only build read terms for packs and registrs that are not in packs.
        isPack = self.hatchNode.__class__.__name__ == Pack.__name__
        isRegister = ((self.hatchNode.parent.__class__.__name__ != Pack.__name__) and (self.hatchNode.__class__.__name__ == Register.__name__))

        if isPack or isRegister:

            #
            # address decode
            #

            self.verilogReadTerm += "      %s : %s <= "%(addr_string(self.hatchNode.properties['base_address'],
                                                                     self.target.localAddressWidth),
                                                         self.target.readData)

            #
            # padd the register or pack to fill the full data bus width.
            #

            if self.hatchNode.properties['bit_width'] > self.target.dataWidth:
                raise AssertionError, hatchNodeErrorString(self.hatchNode, """cannot support %d bit registers, only %d bit registers or 
 less are supported."""%(self.hatchNode.properties['bit_width'], self.target.dataWidth))
            elif self.hatchNode.properties['bit_width'] == self.target.dataWidth:
                # simple case.
                self.verilogReadTerm += "%s;\n"%(self.name)
            else:
                # pad.
                self.verilogReadTerm += "{%d'h0, %s};\n"%(self.target.dataWidth - self.hatchNode.properties['bit_width'],
                                                          self.name)
            

#-----------------------------------------------------------------

def addr_string(a,w=32,m=0xffffffffL):
    """return a string formated as x'hxxx using the address, width and mask supplied."""
    return "%i'h%x"%(w,a&m)

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

def value_string(s, w):
    """return a value formated as a verilog number of the width given.
    If the string given contains an expression, just return the
    expression."""

    if (type(s) == type(0.0) or type(s) == type(0)):
        # is it just a number
        v = int(s)
        negative = False
        if v < 0:
            # it's -ve
            negative = True
            v = abs(v)
        if ((abs(v) > 0) and (int(math.ceil(math.log(abs(v)) / math.log(2))) > w)):
            raise ValueError, "Width of number is bigger than can be accomodated by the register."
        if negative:
            r = "-%d'h%x"%(w, v)
        else:
            r = "%d'h%x"%(w, v)
    elif (type(s) == type('')) :
        # more complicated than just a number.
        m1 = re.match("^\s*(?P<sign>-?)\s*0x(?P<hex>[a-fA-f0-9_]+)\s*$", s)
        m2 = re.match("^\s*(?P<sign>-?)\s*(?P<width>\d+)`h(?P<hex>[a-fA-f0-9_]+)\s*$", s)
        m3 = re.match("^\s*(?P<sign>-?)\s*(?P<width>\d+)`d(?P<dec>[0-9_]+)\s*$", s)
        m4 = re.match("^\s*(?P<sign>-?)\s*(?P<width>\d+)`b(?P<bin>[01_]+)\s*$", s)
        if m1:
            # we have a hex string that did not evaluate for some reason.
            n = m1.group('hex')
            n = n.replace('_', '')
            v = int("0x%s"%(n), 0)
            if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
                raise ValueError, "Width of number is bigger than can be accomodated by the register."
            r = m1.group('sign')+"%d'h%x"%(w, v)
        elif m2:
            # we have a verilog formated hex string.
            n = m2.group('hex')
            n = n.replace('_', '')
            if int(m2.group('bit_width')) != w:
                raise ValueError, "Width of number does not match the register width."
            r = m2.group('sign')+"%d'h%s"%(w, n)
        elif m3:
            # we have a verilog formated decimal string.
            n = m3.group('dec')
            n = n.replace('_', '')
            if int(m3.group('bit_width')) != w:
                raise ValueError, "Width of number does not match the register width."
            v = int(n)
            if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
                raise ValueError, "Width of number is bigger than can be accomodated by the register."
            r = m3.group('sign')+"%d'h%x"%(w, v)
        elif m4:
            # we have a verilog formated binary string.
            n = m4.group('bin')
            n = n.replace('_', '')
            if int(m4.group('bit_width')) != w:
                raise ValueError, "Width of number does not match the register width."
            v = int(n, 2)
            if v > 0 and int(math.ceil(math.log(v) / math.log(2))) > w:
                raise ValueError, "Width of number is bigger than can be accomodated by the register."
            r = m4.group('sign')+"%d'h%x"%(w, v)
        else:
            # I'm assuming this is an expression of some sort.
            r = s
            # XXXX - I should put in expression result width checking
            # here at some time, should also check the varibales in
            # the expresion are available in the module
    else:
        # I dont know what this is!
        raise ValueError, "value_string - dont know what this is :" + s.__str__()

    return r

#-----------------------------------------------------------------
